﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Graphics;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Media.Capture;
using Windows.ApplicationModel.DataTransfer;
using Windows.UI.Xaml.Media;

    class MetroImage
    {
        private WriteableBitmap bitmap;
        public  const int CHANNEL_BLUE = 0;
        public  const int CHANNEL_GREEN = 1;
        public  const int CHANNEL_RED = 2;
        public  const int CHANNEL_ALPHA = 3;
        
        public const int TYPE_RGB=0;

        public const int TYPE_GRAYSCALE=1;

        public int ImType = TYPE_GRAYSCALE;
        private MetroImage()
        {

        }
        
        public int FindType()
        {
            
            return ImType;
        }
        public static async Task<MetroImage> CreateFromFileAsync(Windows.Storage.StorageFile file)
        {
            Windows.Storage.Streams.IRandomAccessStream readFile = await file.OpenReadAsync();




            MetroImage img = new MetroImage();
            img.bitmap = new WriteableBitmap(9,9);
            img.bitmap.SetSource(readFile);
            img.NumberOfColumns = img.bitmap.PixelWidth;
            img.NumberOfRows = img.bitmap.PixelHeight;
           
           // await new Windows.UI.Popups.MessageDialog(img.Height+" +Preparation " + img.PixelHeight).ShowAsync();
            img.readPixelValues();
            return img;
            
        }
        
        public static async Task<MetroImage> CreateFromFileAsync(string filePath)
        {
            Windows.Storage.StorageFile file = await Windows.Storage.StorageFile.GetFileFromPathAsync(filePath);
            return await CreateFromFileAsync(file);
                
        }

        public static MetroImage CreateNew(int w,int h)
        {
            MetroImage img=new MetroImage();
            img.bitmap = new WriteableBitmap(w, h);
            img.stream = img.bitmap.PixelBuffer.AsStream();

            return img;

        }

        

        byte[,,] values;
        Stream stream;
        private async void readPixelValues()
        {
            if (values != null) return;
            stream = bitmap.PixelBuffer.AsStream();
            values = new byte[this.NumberOfRows,this.NumberOfColumns,4];
            stream.Seek(0, 0);
            
            for(int i=0;i<this.NumberOfRows;i++)
            {
                for(int j=0;j<this.NumberOfColumns;j++)
                {
                   
                    values[i,j,0]=(byte)stream.ReadByte();
                    values[i,j,1]=(byte)stream.ReadByte();
                    values[i,j,2]=(byte)stream.ReadByte();
                    values[i,j,3]=(byte)stream.ReadByte();
                    if (values[i, j, 0] != values[i, j, 1] || values[i, j, 1] != values[i, j, 2]) ImType = TYPE_RGB;
                }
               
            }
           // new Windows.UI.Popups.MessageDialog(FindType() +" + Preparation - " + (values.Length/(Height*Width))).ShowAsync();
           
        }

        
        
        private string CollectionAsString(List<byte> set)
        {
            string outp = "<";
            foreach (byte o in set)
            {
                outp += o + ",";
            }
            return outp.Insert(outp.Length - 1, ">");
        }

        public byte At(int r,int c,int ch)
        {
            r = (Math.Abs(r - IndexingBase)) % NumberOfRows;
            c = (Math.Abs(c - IndexingBase) )% NumberOfColumns;
            var vaLen = this.NumberOfColumns * this.NumberOfRows * 4;

            if (4 * r * NumberOfColumns + 4 * c + ch >= vaLen || 4 * r * NumberOfColumns + 4 * c + ch <0)
            {
                 results+="["+vaLen + " +++ " + r+"/"+c+"]";
                return 0;
            }
            return values[r,c,ch];
        }

        
        public void Set(int r, int c, int ch, int value)
        {
            value=value>255 ? 255:(value<0 ? 0:value);
            values[r,c,ch] = (byte)value;
        }
        /////<Negate>
        public void Negate()
        {
            var ind = SetIndexingBase(0);
            for (int i = 0; i < this.NumberOfRows; i++)
            {
                for (int j = 0; j < this.NumberOfColumns; j++)
                {
                    this.Set(i,j,0,255-this.At(i,j,0));
                    this.Set(i, j, 1, 255 - this.At(i, j, 1));
                    this.Set(i, j, 2, 255 - this.At(i, j, 2));
                }
            }
            SetIndexingBase(ind);
        }
        public static MetroImage Negate(MetroImage inp)
        {
            MetroImage outp=inp.Clone();
            outp.Negate();
            outp.Flush();
            return outp;
        }
        /////</Negate>

        /////<ColorToGray>
            public void ColorToGray()
            {
                var ind = SetIndexingBase(0);

                for (int i = 0; i < this.NumberOfRows; i++)
                {
                    for (int j = 0; j < this.NumberOfColumns; j++)
                    {
                        byte b = this.At(i, j, 0);
                        byte g = this.At(i, j, 1);
                        byte r = this.At(i, j, 2);

                        int gray = (byte)((0.311 * r) + (0.486 * g) + (0.213 * b));     
                        this.Set(i, j, 0, gray);
                        this.Set(i, j, 1, gray);
                        this.Set(i, j, 2, gray);
                    }
                }


                SetIndexingBase(ind);
            }
            public static MetroImage ColorToGray(MetroImage inp)
            {
                MetroImage outp = inp.Clone();
                outp.ColorToGray();
                outp.Flush();
                return outp;
            }

        /////</ColorToGray>
        /////<ApplyFilter>
         public void ApplyFilter(float[,] filter)
         {
             var ind = SetIndexingBase(0);

             for (int i = 0; i < this.NumberOfRows; i++)
             {
                 for (int j = 0; j < this.NumberOfColumns; j++)
                 {
                     float red = 0;
                     float blue = 0;
                     float green = 0;
                     for (int fi = 0; fi < filter.GetLength(0); fi++)
                     {
                         for (int fj = 0; fj < filter.GetLength(1); fj++)
                         {
                             int pi = (i - filter.Length / 2 + fi ); 
                             int pj = (j - filter.Length / 2 + fj ); 
                              red += this.At(pi,pj,CHANNEL_RED) * filter[fi,fj];
                              green += this.At(pi, pj, CHANNEL_GREEN) * filter[fi,fj];
                              blue += this.At(pi, pj, CHANNEL_BLUE) * filter[fi,fj]; 

                           //    green += image[imageX][imageY].g * filter[filterX][filterY]; 
                            //   blue += image[imageX][imageY].b * filter[filterX][filterY]; 
                         }

                         
                     }

                     this.Set(i, j, CHANNEL_RED, (int)red);
                     this.Set(i, j, CHANNEL_GREEN, (int)green);
                     this.Set(i, j, CHANNEL_BLUE, (int)blue);
                 }
             }





             SetIndexingBase(ind);
         }
        public static MetroImage ApplyFilter(MetroImage inp,float[,] filter)
         {
             MetroImage outp = inp.Clone();
             outp.ApplyFilter(filter);
             outp.Flush();
             return outp;
         }
        /////<ApplyFilter>
        public MetroImage Clone()
        {
            MetroImage clone = new MetroImage();
            clone.NumberOfColumns = this.NumberOfColumns;
            clone.NumberOfRows = this.NumberOfRows;
            clone.bitmap = new WriteableBitmap(NumberOfColumns, NumberOfRows);
            clone.values=new byte[this.NumberOfRows,this.NumberOfColumns,4];
            clone.stream = clone.bitmap.PixelBuffer.AsStream();
            for (int i = 0; i < this.NumberOfRows; i++)
            {
                for (int j = 0; j < this.NumberOfColumns; j++)
                {
                    for (int k = 0; k < 4; k++)
                    {
                        clone.values[i, j, k] = this.values[i, j, k];
                    }
                }
                
            }
            clone.Flush();

            return clone;
        }

       

        public void Flush()
        {
            stream.Seek(0, 0);
            for (int i = 0; i < this.NumberOfRows; i++)
            {
                for (int j = 0; j < this.NumberOfColumns; j++)
                {
                    for (int k = 0; k < 4; k++)
                    {
                        stream.WriteByte(this.values[i, j, k]);
                    }
                }
            }
            bitmap.Invalidate();
        }
        private int NumberOfRows=0;
        private int NumberOfColumns=0;
        private static int IndexingBase=0;
        public string results="";
        public int getHeight() {
            return this.NumberOfRows;
        }

        public int getWidth() {
            return this.NumberOfColumns;
            
        }
        public ImageSource AsImageSource()
        {
            return bitmap;
        }
        public static int SetIndexingBase(int newBase) {
            int old = IndexingBase;
            IndexingBase = newBase;
            return old;
        }

        public static int GetIndexingBase()
        {
           
            return IndexingBase;
        }
    }

